home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
admin
/
linuxcon.000
/
linuxcon
/
linuxconf-1.6
/
fstab
/
fixperm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-05-18
|
13KB
|
558 lines
#include <limits.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include "fstab.h"
#include "../userconf/userconf.h"
#include "../misc/misc.h"
#include "../netconf/netconf.h"
#include "../paths.h"
#include "fstab.m"
/* #Specification: fixperm / strategy
The directory /usr/lib/linuxconf/conf.permissions contains
files describing the permissions of different packages.
The format of the file is rather simple.
#
filespec owner group type permissions options ...
#
The permissions are expressed in octal.
type is one of those:
#
b,major,minor: block device
c,major,minor: character device
f: file
d: directory
#
Here are the options supported
#
boot: The check is done only at boot time. This is
especially intended for pseudo tty which need
special permissions. Those pty generally change
while the system is running. Normally, everything
is back to normal when the system goes down, except
when it crash. Unless the permissions are set
correctly, everything goes wrong: Debuggers, some
editors etc...
recurse: filespec must be a directory. The
owner, group and permissions will be
applied to all file in that directory
and sudirectory.
required: The file has to be there. An error
will be signaled if not. If the file
is a directory, it will be created.
#
*/
#
const int maxopt=3;
struct SPEC_ONE{
const char *fpath; // Path of the specification file
int noline; // Current line in fpath
char path[PATH_MAX];
char owner[PATH_MAX];
char group[PATH_MAX];
char type[PATH_MAX];
int perm;
char opts[maxopt][PATH_MAX];
};
/*
Print an error message with reference to the specification file
*/
static void fixperm_error (SPEC_ONE &sp, const char *msg, ...)
{
va_list list;
va_start (list,msg);
char buf[1000];
vsprintf (buf,msg,list);
va_end (list);
xconf_error (MSG_U(E_CONFFILE,"%s\n\n"
"In file %s, line %d\n"),buf,sp.fpath,sp.noline);
}
struct FIXPERM_OPTIONS{
bool boot;
bool recurse;
bool required;
};
enum FPERM_TYPE { FPERM_FILE, FPERM_DIR};
class FIXPERM_SPEC: public ARRAY_OBJ{
friend class FIXPERM_SPECS;
SSTRING path;
SSTRING owner;
SSTRING group;
FPERM_TYPE type;
int perm;
int dev;
FIXPERM_OPTIONS opts;
int uid;
int gid;
bool valid; // Is this record valid
/*~PROTOBEG~ FIXPERM_SPEC */
public:
FIXPERM_SPEC (SPEC_ONE&sp);
int check (void);
int create (void);
private:
void parsedev (SPEC_ONE&sp);
public:
/*~PROTOEND~ FIXPERM_SPEC */
};
/*
Parse the option of one spec
*/
static int fixperm_parseopts (SPEC_ONE &sp, FIXPERM_OPTIONS &opts)
{
int ret = 0;
opts.boot = opts.recurse = opts.required = false;
for (int i=0; i<maxopt; i++){
if (sp.opts[i][0] == '\0'){
break;
}else if (strcmp(sp.opts[i],"recurse")==0){
opts.recurse = true;
}else if (strcmp(sp.opts[i],"required")==0){
opts.required = true;
}else if (strcmp(sp.opts[i],"boot")==0){
opts.boot = true;
}else{
ret = -1;
fixperm_error (sp,MSG_U(E_IVLDOPT,"Invalid option %s")
,sp.opts[i]);
}
}
return ret;
}
/*
Parse a special device spec (b|c,major,minor)
Return -1 if any error or the encoded device number.
*/
PRIVATE void FIXPERM_SPEC::parsedev (SPEC_ONE &sp)
{
dev = -1;
const char *type = sp.type + 2;
if (isdigit(type[0])){
int major = atoi (type);
type = str_skipdig(type);
if (type[0] == ',' && isdigit(type[1])){
int minor = atoi(type+1);
dev = major*256 + minor;
}
}
if (dev == -1){
fixperm_error (sp,MSG_U(E_IVLDDEV
,"Invalid device specification\n"
"Expect b|c,major,minor, got %s\n"),sp.type);
}
}
PUBLIC FIXPERM_SPEC::FIXPERM_SPEC(SPEC_ONE &sp)
{
path.setfrom(sp.path);
owner.setfrom (sp.owner);
group.setfrom (sp.group);
perm = sp.perm;
dev = 0;
valid = true;
if (strcmp(sp.type,"d")==0){
perm |= S_IFDIR;
}else if (strcmp(sp.type,"f")==0){
perm |= S_IFREG;
}else if (strncmp(sp.type,"c,",2)==0){
perm |= S_IFCHR;
parsedev (sp);
}else if (strncmp(sp.type,"b,",2)==0){
perm |= S_IFBLK;
parsedev (sp);
}else{
fixperm_error (sp,MSG_U(E_IVLDTYPE
,"Invalid type field \"%s\" (b, c, d or f expected)\n")
,sp.type);
valid = false;
}
if (fixperm_parseopts(sp,opts)==-1){
valid = false;
}
struct passwd *p = getpwnam (sp.owner);
struct group *g = getgrnam (sp.group);
uid = gid = 0;
if (p == NULL){
fixperm_error (sp,MSG_U(E_NOUSER
,"No user \"%s\" defined on this system")
,sp.owner);
valid = false;
}else{
uid = p->pw_uid;
}
if (g == NULL){
fixperm_error (sp,MSG_U(E_NOGROUP
,"No group \"%s\" defined on this system")
,sp.group);
valid = false;
}else{
gid = g->gr_gid;
}
}
#if 0
PUBLIC void FIXPERM_SPEC::print (FILE *fout)
{
fprintf (fout,"%s %s %s %o"
,path.get(),owner.get(),group.get(),perm);
if (opts.recurse) fputs (" recurse",fout);
if (opts.required) fputs (" required",fout);
fputc ('\n',fout);
}
#endif
/*
Create an empty file or directory
Return -1 if any error.
*/
PUBLIC int FIXPERM_SPEC::create()
{
int ret = 0;
const char *fpath = path.get();
if (S_ISDIR(perm)){
ret = mkdir (fpath,0666);
}else if (S_ISCHR(perm) || S_ISBLK(perm)){
ret = mknod (fpath,perm,dev);
}else{
int fd = creat (fpath,O_WRONLY);
if (fd != -1){
close(fd);
}else{
ret = -1;
}
}
if (ret != 0
|| chown (fpath,uid,gid) == -1
|| chmod (fpath,perm) == -1){
ret = -1;
}
return ret;
}
/*
Fix one file specification
*/
PUBLIC int FIXPERM_SPEC::check()
{
if (valid){
bool dook = !simul_ison();
struct stat st;
const char *fpath = path.get();
if (stat(fpath,&st)==-1){
/* #Specification: fixperm / symbolic links
Symbolic links are often used to move things
around in different file systems. The symbolic
links are faking the original hierarchy. This complicates
life for the "fixperm" functionnality of linuxconf.
In fact, in different time, linuxconf can't produce
a reliable status.
For example my mail spool directory is on an NFS server.
Instead of mounting the server spool in /var/spool/mail,
I use the amd automounter and set a symbolic links like
this.
ln -s /n/server/var/spool/mail /var/spool/mail
Unfortunatly, linuxconf can't test the validity
of /var/spool/mail until the network is up. This
checking is annoying. It complains that /var/spool/mail
is not a directory and so on.
So here is the patch. If a "something" has been
replaced by a symbolic links and the destination
of the link appears to be missing, linuxconf
won't complain at all.
*/
if (opts.required && lstat(fpath,&st)==-1){
net_prtlog (MSG_U(L_CREATING
,"Creating %s %s\n")
,S_ISDIR(perm)
? MSG_U(L_DIRECTORY,"directory")
: MSG_U(L_FILE,"file")
,fpath);
if (dook) create();
}
}else if ((st.st_mode & S_IFMT)
!= (perm & S_IFMT)){
net_prtlog (
MSG_U(E_CANTCHG
,"**** Can't change the type of file %s\n"
" manual action required!\n")
,fpath);
}else if ((S_ISBLK(perm) || S_ISCHR(perm))
&& st.st_rdev != dev){
net_prtlog (MSG_U(E_WRONGDEV,"Device file %s wrongly created\n"
"\tExpected major %d, minor %d\n"
"\tFound major %d, minor %d\n"
"\tManual action required\n")
,fpath,dev>>8,dev&0xff,st.st_rdev>>8,st.st_rdev&0xff);
}else{
if (st.st_uid != uid
|| st.st_gid != gid){
if (dook) chown (fpath,uid,gid);
net_prtlog (MSG_U(L_CHGOWN,"Changing owner of file %s "
"to %s.%s\n")
,fpath,owner.get(),group.get());
stat(fpath,&st);
}
if (st.st_mode != perm){
if (dook) chmod (fpath,perm);
net_prtlog (MSG_U(L_CHGPERM
,"Changing permissions of file %s "
"from %o to %o\n")
,fpath,st.st_mode,perm);
}
}
}
return 0;
}
class FIXPERM_SPECS: public ARRAY{
/*~PROTOBEG~ FIXPERM_SPECS */
public:
FIXPERM_SPECS (const char *fname);
int check (bool boottime);
int check (void);
FIXPERM_SPEC *getitem (int no);
/*~PROTOEND~ FIXPERM_SPECS */
};
/*
Check and optionnally ajust permissions on some files
*/
PUBLIC FIXPERM_SPECS::FIXPERM_SPECS(const char *fname)
{
SPEC_ONE sp;
sp.fpath = fname;
sp.noline = 1;
FILE *fin = xconf_fopen (sp.fpath,"r");
char buf[500];
/* #Specification: fixperm / file format / comments
The fixperm's spec file may contain blank lines and
line beginning with a # are comments.
*/
while (fgets (buf,sizeof(buf)-1,fin)!=NULL){
strip_end (buf);
char *ptbuf = str_skip(buf);
if (ptbuf[0] != '\0' && ptbuf[0] != '#'){
sp.opts[0][0] = sp.opts[1][0] = sp.opts[2][0] = '\0';
if (sscanf(buf,"%s %s %s %s %o %s %s %s\n"
,sp.path,sp.owner,sp.group,sp.type,&sp.perm
,sp.opts[0],sp.opts[1],sp.opts[2]) < 5){
fixperm_error (sp,MSG_U(E_IVLDLINE,"Invalid line"));
}else{
add (new FIXPERM_SPEC (sp));
}
sp.noline++;
}
}
}
PUBLIC FIXPERM_SPEC *FIXPERM_SPECS::getitem(int no)
{
return (FIXPERM_SPEC*)ARRAY::getitem(no);
}
PUBLIC int FIXPERM_SPECS::check(bool boottime)
{
int ret = 0;
for (int i=0; i<getnb(); i++){
FIXPERM_SPEC *chk = getitem(i);
/* #Specification: fixperm / boot time check
Some checking is done only at boot time
because permission and ownership change while
working. This is especially true for pty's.
*/
if (chk->opts.boot){
if (boottime){
if (chk->check() == -1) ret = -1;
}
}else{
if (chk->check() == -1) ret = -1;
}
}
return ret;
}
PUBLIC int FIXPERM_SPECS::check()
{
return check (false);
}
static int fixperm_check (
const char *prefix,
SSTRINGS &lst,
bool boottime)
{
int ret = 0;
for (int i=0; i<lst.getnb(); i++){
const char *ptf = lst.getitem(i)->get();
if (ptf[0] != '\0'){
char path[PATH_MAX];
sprintf (path,"%s/%s",prefix,ptf);
FIXPERM_SPECS specs(path);
if (specs.check(boottime) == -1) ret = -1;
}
}
return ret;
}
/*
Check and optionnally ajust permissions on some files
Return -1 if any error.
*/
static int fixperm_check(bool boottime)
{
/* #Specification: fixperm / /var/lib/conf.permissions / strategy
Linuxconf provide its own set of permissions files
int /usr/lib/linuxconf/conf.permissions. These are
supplied with linuxconf and will probably get updated
at each new releases.
You can create permission files in /var/lib/conf.permissions.
If you create a permission file with the same name as
one in /usr/lib/linuxconf/conf.permissions, yours will
take precedence. This allows you to do customisation
of those file without fearing the next update :-)
*/
SSTRINGS usr_tb;
dir_getlist(USR_LIB_CONF_PERMISSIONS,usr_tb);
SSTRINGS var_tb;
dir_getlist(VAR_LIB_CONF_PERMISSIONS,var_tb);
for (int v=0; v<var_tb.getnb(); v++){
int u = usr_tb.lookup(var_tb.getitem(v));
if (u != -1) usr_tb.getitem(u)->setfrom("");
}
return fixperm_check (USR_LIB_CONF_PERMISSIONS,usr_tb,boottime)
| fixperm_check (VAR_LIB_CONF_PERMISSIONS,var_tb,boottime);
}
/*
Check and optionnally ajust permissions on some files
Return -1 if any error.
*/
int fixperm_check()
{
return fixperm_check(false);
}
/*
Check and optionnally ajust permissions on some files at boot time
Return -1 if any error.
*/
int fixperm_check_boot()
{
simul_init();
int ret = fixperm_check(true);
if (ret != -1 && simul_prompt()==1){
ret = fixperm_check(true);
}
return ret;
}
/*
Check and optionnally ajust permissions on some system
*/
static int fixperm_check(int nb, char *tb[])
{
int ret;
/* #Specification: fixperm / command line / args
"fixperm --update" or "fixperm --status" without
further arguments will check all system in
/usr/lib/linuxconf/conf.permissions/
and /var/lib/conf.permissions/.
If some argument are provided, they are taken
as specific system to check in either
/usr/lib/linuxconf/conf.permissions/ or
/var/lib/conf.permissions/.
If one argument start with a /, it is taken as an absolute
path to a specification file using the same format
as the ones in /usr/lib/linuxconf/conf.permissions/.
*/
if (nb == 0){
ret = fixperm_check(); // Check all
}else{
ret = 0;
SSTRINGS usr_tb;
dir_getlist(USR_LIB_CONF_PERMISSIONS,usr_tb);
SSTRINGS var_tb;
dir_getlist(VAR_LIB_CONF_PERMISSIONS,var_tb);
for (int i=0; i<nb; i++){
char *arg = tb[i];
char path[PATH_MAX];
if (arg[0] == '/'){
strcpy (path,arg);
}else if (var_tb.lookup(arg) !=-1){
sprintf (path,"%s/%s",VAR_LIB_CONF_PERMISSIONS
,arg);
}else if (usr_tb.lookup(arg) !=-1){
sprintf (path,"%s/%s",USR_LIB_CONF_PERMISSIONS
,arg);
}
FIXPERM_SPECS specs(path);
if (specs.check() == -1) ret = -1;
}
}
return ret;
}
static void usage()
{
fprintf (stderr,
"fixperm --status\n"
"fixperm --update [ system ... ]\n"
);
}
int fstab_fixperm (int argc, char *argv[])
{
int ret = -1;
if (argc == 1){
usage();
}else{
if (strcmp(argv[1],"--status")==0){
simul_init(stdout);
ret = fixperm_check (argc-2,argv+2);
}else if (strcmp(argv[1],"--update")==0){
if (perm_rootaccess("set file permissions")){
net_introlog ("fixperm --update");
ret = fixperm_check (argc-2,argv+2);
}else{
ret = -1;
}
}else{
usage();
}
}
return ret;
}